﻿using System;
using System.Runtime.InteropServices;

namespace DotNetDetour.DetourWays
{
    public unsafe class NativeDetourFor64Bit : NativeDetourFor32Bit
    {
        //14
        private readonly byte[] jmp_inst =
        {
      0xFF,0x25,0x00,0x00,0x00,0x00,  //jmp qword ptr[RelativeAddr]
      0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90 //target_addr
    };

        protected override IntPtr SolveJmpIns(IntPtr ptr)
        {
            byte* p = (byte*)ptr;

            while (true)
            {
                p = (byte*)base.SolveJmpIns((IntPtr)p);
                //http://bbs.chinaunix.net/thread-2012474-1-1.html

                var p2 = p;
                if ((*p2 & 0b0100_0000) != 0)
                {
                    p2++;
                }
                if (*p2 == 0xff && *(p2 + 1) == 0x25)
                {
                    //jmp qword ptr[RelativeAddr]
                    //target_addr
                    p = (byte*)*(ulong*)(p2 + *(int*)(p2 + 2) + 6);
                }
                else
                    break;
            }
            return (IntPtr)p;
        }

        protected override void MakeJmp(IntPtr from, IntPtr to, bool foceE9Jmp)
        {
            long relative = (long)to - (long)from - 5;
            if (relative > int.MaxValue || relative < int.MinValue)
            {
                //如果跳转太远
                if (foceE9Jmp)
                {
                    var pTran = Tranpoline.Alloc(from);
                    _hookApiInfos[_hookApiInfos.Count - 1].Tranpoline.Add(pTran);

                    base.MakeJmp(from, pTran, foceE9Jmp);
                    var p = (long*)(pTran + 6);
                    Marshal.Copy(jmp_inst, 0, pTran, jmp_inst.Length);
                    *p = (long)to;
                }
                else
                {
                    var p = (long*)(from + 6);
                    Marshal.Copy(jmp_inst, 0, from, jmp_inst.Length);
                    *p = (long)to;
                }
            }
            else
            {
                base.MakeJmp(from, to, foceE9Jmp);
            }
        }
    }
}